package com.github.websocket;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.DataFetcher;
import com.github.bean.CallLog;
import com.github.bean.ServerUser;
import com.github.bean.ServerUserSession;
import com.github.common.constant.MyConstant;
import com.github.manager.Manager;
import com.github.pojo.*;
import com.github.service.CallLogServer;
import com.github.service.UserServer;
import com.github.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.stream.Collectors;

@Component("wsHandler")
public class WSHandler extends TextWebSocketHandler {
    // 在控制器或服务中添加测试
    private final Logger logger = LoggerFactory.getLogger(getClass());

    private final ObjectMapper objectMapper = new ObjectMapper();
    //    ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
    ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors());

    @Autowired
    private SocketSendThread socketSendThread;
    @Autowired
    @Qualifier("userStatusWebSocketHandler")
    private UserStatusWebSocketHandler userStatusWebSocketHandler;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private CallLogServer callLogServer;

    public WSHandler() {
        startHeartbeatChecker();
    }

    private void startHeartbeatChecker() {
        scheduler.scheduleAtFixedRate(() -> {
            try {
                List<ServerUserSession> allUserSession = SessionSocketHolder.getAllUserSession();
                long currentTime = System.currentTimeMillis();
                if (CollUtil.isEmpty(allUserSession)) {
                    return;
                }
                for (ServerUserSession userSession : allUserSession) {
                    long lastHeartbeatTime = userSession.getLastHeartbeatTime();
                    ServerUserSession serverUserSession = SessionSocketHolder.get(userSession.getAccount(), Role.SERVER);

                    if (serverUserSession != null && serverUserSession.isOnline()) { //拉手在线
                        if (userSession.getDeviceType().equals(Role.CALL) || userSession.getDeviceType().equals(Role.VOICE)) {
                            if (userSession.getOfflineNum() < 2 &&
                                    !userSession.isOnline() &&
                                    (currentTime - lastHeartbeatTime) > userSession.getTimeout() * 1000L) {
                                userSession.setOfflineNum(userSession.getOfflineNum() + 1);
                                logger.info("心跳超时：通知拉手--account =" + userSession.getAccount() + ";设备：" + userSession.getDeviceType());
                                Utils.toServerSendMsg(userSession.getAccount(), WsResult.deviceOffline(userSession.getDeviceType()));
                            }
                        }
                    }
                    if (userSession.isOnline()
                            && lastHeartbeatTime > 0
                            && (currentTime - lastHeartbeatTime) > userSession.getTimeout() * 1000L) {
                        logger.info("心跳超时：account =" + userSession.getAccount());
                        if (userSession.isOnline()) {
                            userSession.getSession().close();
                        }
                        userSession.setLastHeartbeatTime(0);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                logger.info("心跳检测：异常-->" + e.getMessage());
            }
        }, 1, 1, TimeUnit.SECONDS);
        logger.info("Heartbeat checker started.");
    }


    // 当有新的WebSocket连接建立时触发
    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        try {
            URI uri = session.getUri();
            if (uri == null) {
                //关闭连接
                Utils.delayedCheck(session, 1000);
                return;
            }
            //添加设备单独线程
            socketSendThread.add(session);

            logger.info("新设备连接: session：" + session.getId() + ";uri" + uri);
            String query = uri.getQuery();
            String device_type = Utils.getParamValue(query, "device_type");

            String jsonData = "";
            if (device_type.equals(Role.SERVER)) { //拉手
                String token = Utils.getParamValue(query, "token");
                jsonData = objectMapper.writeValueAsString(WsResult.success(1));
                //通过登录接口的token获取账号
                String account = (String) stringRedisTemplate.opsForHash().get(MyConstant.UserLoginToken, token);
                //如果没有获取到账号说明没有登陆，添加逻辑让客户退出到登陆界面
                if (account == null || account.isEmpty()) {
                    logger.info("account为null, 请重新登录");
                    Utils.toSessionSendMsg(session, WsBaseResult.kick());
                    Utils.delayedCheck(session, 1000);
                } else {
                    SessionSocketHolder.put(account, device_type, session);
                    ServerUserSession serverUser = SessionSocketHolder.get(account, device_type);
                    if (serverUser != null) {
                        logger.info("拉手端上线 ---> 通知其他端 拉手端已上线");
                        Runnable task = () -> {
                            Utils.checkDevice(account);
                        };
                        scheduler.schedule(task, 1, TimeUnit.SECONDS);
                    }
                    //updateUserStatus(serverUser);
                    logger.info("服务端上线 ==> Token " + token);
                }
            } else if (device_type.equals(Role.VOICE) || device_type.equals(Role.CALL)) { //语音  或  插卡
                String account = Utils.getParamValue(query, "account");

                Utils.toServerSendMsg(account, WsResult.deviceOnline(device_type));

                SessionSocketHolder.put(account, device_type, session);
                logger.info("设备上线  account={},device_type={}", account, device_type);

                jsonData = objectMapper.writeValueAsString(WsResult.success(4));
            } else {
                logger.info("未知端: " + query);
                //未知端，断开连接
                Utils.delayedCheck(session, 1000);
            }
            logger.info("新设备连接: jsonData" + jsonData);
            SocketSendThread.sendMessage(session, jsonData);
        } catch (Exception e) {
            e.printStackTrace();
            logger.info("设备上线异常: " + e.getMessage());
        }

    }


    // 当接收到客户端发送的消息时触发
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) {
        try {
            String msg = AESEncrypt.decrypt(message.getPayload());
            JsonNode rootNode = objectMapper.readTree(msg);
            int msg_type = rootNode.path("msg_type").asInt();
            if (msg_type != 2) {
                logger.info("收到消息: msg-->" + msg);
            }
            ServerUserSession serverSession = SessionSocketHolder.get(session.getId());

            //如果没有获取到serverUser说明没有正确连接，添加逻辑让客户退出到登陆界面
            if (serverSession == null) {
                logger.info("serverUser为null, 请重新登录");
                Utils.toSessionSendMsg(session, WsBaseResult.kick());
                Utils.delayedCheck(session, 1000);
            } else {
                String device_type = "-1";
                if (rootNode.has("device_type")) {
                    device_type = rootNode.path("device_type").asText();
                }
                String jsonString = "";
                switch (msg_type) {
                    case 2:
                        jsonString = objectMapper.writeValueAsString(WsBaseResult.pong());
                        serverSession.setLastHeartbeatTime(System.currentTimeMillis()); // 更新心跳时间戳
                        break;
                    case 3:
                        JsonNode msgNode = rootNode.path("msg_data");
                        if (device_type.equals(Role.SERVER)) {
                            if (msgNode.has("room_id")) {
                                long requestID = rootNode.path("request_id").asLong();
                                String room_id = msgNode.path("room_id").asText();
                                String stream_id = msgNode.path("stream_id").asText();
                                logger.info("收到消息: 拉手请求加入房间，room_id={},stream_id={},account={}", room_id, stream_id,serverSession.getAccount());
                                Utils.toVoiceSendMsg(serverSession.getAccount(), WsMsgResult.success(new Room(room_id, stream_id, 4)));
                                Utils.toServerSendMsg(serverSession.getAccount(), WsDataResult.success(requestID, ResStatus.success(4)));
                            } else if (msgNode.has("remote_call")) {
                                long requestID = rootNode.path("request_id").asLong();
                                serverSession.setLastRequestStartCallToCard(requestID);
                                String remoteCall = msgNode.path("remote_call").asText();
                                CallLog callLog = new CallLog(requestID, remoteCall, serverSession.getAccount(), System.currentTimeMillis());
                                Runnable task = () -> {
                                    Utils.toCallSendMsg(serverSession.getAccount(), WsMsgResult.success(requestID, new RemoteCall(remoteCall, 1)));
                                };
                                logger.info("收到消息: 拉手请求呼叫，remote_call={},account={}", remoteCall, serverSession.getAccount());
                                scheduler.schedule(task, Manager.delay, TimeUnit.SECONDS);
                                // 回复服务端通知成功
                                Utils.toServerSendMsg(serverSession.getAccount(), WsDataResult.success(requestID, ResStatus.success(1)));
                                callLogServer.addCallLog(callLog);
                            } else if (msgNode.has("hang_up")) {
                                // 接到服务端挂电话
                                long requestID = rootNode.path("request_id").asLong();
                                boolean hangUp = msgNode.path("hang_up").asBoolean();
                                int hangUpType = msgNode.path("hang_up_type").asInt();
                                // 通知插卡端挂断电话
                                Utils.toCallSendMsg(serverSession.getAccount(), WsMsgResult.success(new CallStatus(hangUp, hangUpType, 3)));
                                // 回复服务端通知成功
                                Utils.toServerSendMsg(serverSession.getAccount(), WsDataResult.success(requestID, ResStatus.success(3)));
                                logger.info("收到消息: 拉手挂断电话，通知插卡端挂断电话，回复服务端通知成功,account={}", serverSession.getAccount());
                                callLogServer.setEndTime(serverSession.getLastRequestStartCallToCard(), System.currentTimeMillis());
                                callLogServer.setMsg(serverSession.getLastRequestStartCallToCard(), "", false);
                            }
                        } else if (device_type.equals(Role.CALL)) {
                            if (msgNode.has("telephone_number")) {
                                String telephoneNumber = msgNode.path("telephone_number").asText();
                                Utils.toServerSendMsg(serverSession.getAccount(), WsMsgResult.success(new Telephone(telephoneNumber, 2)));
                            } else if (msgNode.has("hang_up")) {
                                // 接到插卡端，挂电话通知
                                long requestID = rootNode.path("request_id").asLong();
                                boolean hangUp = msgNode.path("hang_up").asBoolean();
                                int hangUpType = msgNode.path("hang_up_type").asInt();
                                // 通知服务端挂断电话
                                Utils.toServerSendMsg(serverSession.getAccount(), WsMsgResult.success(new CallStatus(hangUp, hangUpType, 3)));
                                // 回复插卡端通知成功
                                Utils.toCallSendMsg(serverSession.getAccount(), WsDataResult.success(requestID, ResStatus.success(3)));
                                logger.info("收到消息: 插卡挂断电话，通知服务端挂断电话，回复插卡端通知成功,account={}", serverSession.getAccount());
                                callLogServer.setEndTime(serverSession.getLastRequestStartCallToCard(), System.currentTimeMillis());
                                callLogServer.setMsg(serverSession.getLastRequestStartCallToCard(), "", false);
                            }
                        } else if (device_type.equals(Role.VOICE)) {
                            if (msgNode.has("room_id")) {
                                long requestID = rootNode.path("request_id").asLong();
                                String room_id = msgNode.path("room_id").asText();
                                String stream_id = msgNode.path("stream_id").asText();
                                Utils.toServerSendMsg(serverSession.getAccount(), WsMsgResult.success(new Room(room_id, stream_id, 4)));
                                Utils.toVoiceSendMsg(serverSession.getAccount(), WsDataResult.success(requestID, ResStatus.success(4)));
                                logger.info("收到消息: 语音请求加入房间，room_id={},stream_id={},account={}", room_id, stream_id, serverSession.getAccount());
                            }
                        } else {
                            logger.info("未知设备类型: " + device_type);
                            return;
                        }
                        break;
                    case 4:
                        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
                        WsResult<?> readValue = objectMapper.readValue(msg, WsResult.class);
                        Utils.toServerSendMsg(serverSession.getAccount(), WsResult.deviceOffline(readValue.getDevice_type()));
                        session.close();
                        break;
                    case 5:
                        jsonString = objectMapper.writeValueAsString(WsBaseResult.pong());
                        break;
                    case 8:
                        String forwardMsg = rootNode.path("forward_msg").path("msg").asText();
                        if (device_type.equals(Role.CALL) && forwardMsg.contains("本次呼叫号码被篡改")) {
                            callLogServer.setMsg(serverSession.getLastRequestStartCallToCard(), forwardMsg, true);
                        } else if (device_type.equals(Role.CALL) && forwardMsg.contains("插卡端默认拨号应用已被篡改")) {
                            callLogServer.setMsg(serverSession.getLastRequestStartCallToCard(), forwardMsg, true);
                        } else
                            Utils.toServerSendMsg(serverSession.getAccount(), ForwardMsg.sendMsg(forwardMsg));
                }
                if (!jsonString.isEmpty()) {
                    if (msg_type != 2) {
                        logger.info("回复消息: " + jsonString);
                    }
                    SocketSendThread.sendMessage(session, jsonString);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.info("接收数据处理异常: " + e.getMessage());
        }

    }

    // 当WebSocket连接关闭时触发
    @Override
    public void afterConnectionClosed(WebSocketSession session, org.springframework.web.socket.CloseStatus status) throws Exception {
        try {
            //删除消息队列
            SocketSendThread.remove(session);
            //删除连接
            SessionSocketHolder.remove(session.getId());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}